summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-04-04 01:48:50 +0200
committerAnton Luka Šijanec <anton@sijanec.eu>2022-04-04 01:48:50 +0200
commitc7c4c0d5f101a2dbaaf72c19a6a573cb5e725511 (patch)
treeb17f733a41443745bd14ada61c73468e34699e33
parentnekaj malega, grem domov (diff)
downloaddiscord.c-dev.tar
discord.c-dev.tar.gz
discord.c-dev.tar.bz2
discord.c-dev.tar.lz
discord.c-dev.tar.xz
discord.c-dev.tar.zst
discord.c-dev.zip
-rw-r--r--.gitignore2
-rw-r--r--src/api.c139
-rwxr-xr-xsrc/generate_parse_functions.php112
-rw-r--r--src/h.c15
-rw-r--r--src/ui.c29
5 files changed, 199 insertions, 98 deletions
diff --git a/.gitignore b/.gitignore
index 06a0a6d..f82c10c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,6 @@ analysis.txt
# files I like to keep in my WD
src.old/
src/ui.glade~
-src/#ui.glade#
+src/#*#
# tmp
tmp/
diff --git a/src/api.c b/src/api.c
index c5fb0b3..2344bf4 100644
--- a/src/api.c
+++ b/src/api.c
@@ -48,9 +48,9 @@ void dc_api_stack (struct dc_api_io i) { /* stack output struct to be delivered
}
unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_channel * c) {
unsigned long long int p = 0; /* note: this is NOT according to server's implementation of */
- struct dc_role ** role = &c->guild->role; /* perm parsing, but should suffice 4 most cases */
- if (!*role) /* see struct dc_guild: if NULL then assume all permissions - a DM guild */
+ if (!c->guild) /* see struct dc_channel: if NULL then assume all permissions - a DM guild */
return DC_ALL_PERMISSIONS;
+ struct dc_role ** role = &c->guild->role; /* perm parsing, but should suffice 4 most cases */
while (*role) {
if (DC_ROLE_EVERYONE(*role)
|| dc_find_user((*role)->users, (*role)->users_length, u->id))
@@ -72,25 +72,53 @@ unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_c
}
return p;
}
-struct dc_user * dc_parse_user (struct dc_user * dst, const cJSON * src) {
- char * cp;
- if (!dst)
- dst = dc_user_init();
- if ((cp = cJSON_GSV(cJSON_GOI(src, "username"))))
- dst->username = strdup(cp);
- if ((cp = cJSON_GSV(cJSON_GOI(src, "discriminator"))))
- dst->discriminator = atoi(cp);
- if ((cp = cJSON_GSV(cJSON_GOI(src, "id"))))
- dst->id = strtoull(cp, NULL, 10);
- if (!dst->username || dst->discriminator == -1) { /* it's quite useless to store only ids */
- dc_user_free(dst, DC_UNSET);
+struct dc_user * dc_parse_user (struct dc_program * p, const cJSON * s) {
+ if (!s) /* in case cJSON_GOIx returns NULL, it will most of the times, this isn't a bug */
+ return NULL;
+ char * c = cJSON_GSV(cJSON_GOI(s, "id"));
+ if (!c)
+ return NULL;
+ unsigned long long int i = strtoull(c, NULL, 10);
+ struct dc_user * u = dc_find_user(p->users, p->users_length, i);
+ if (!u) {
+ u = dc_user_init();
+ u->id = i;
+ dc_addr_user(p, DC_ISAE(p->users), u, 0);
+ }
+ if ((c = cJSON_GSV(cJSON_GOI(s, "username"))) && strlen(c)) {
+ free(u->username);
+ u->username = strdup(c);
+ }
+ if ((c = cJSON_GSV(cJSON_GOI(s, "discriminator"))))
+ u->discriminator = atoi(c);
+ return u;
+}
+struct dc_channel * dc_parse_channel (struct dc_program * p, const cJSON * s) {
+ cJSON * o;
+ if (!s) /* in case cJSON_GOIx returns NULL */
+ return NULL;
+ char * c = cJSON_GSV(cJSON_GOI(s, "id"));
+ if (!c)
return NULL;
+ if (!(DC_CHANNEL_SUPPORTED(cJSON_GNV(cJSON_GOI(s, "type"))))) return NULL;
+ unsigned long long int i = strtoull(c, NULL, 10);
+ struct dc_channel * u = dc_find_channel(p->channels, p->channels_length, i);
+ if (!u) {
+ u = dc_channel_init();
+ u->id = i;
+ dc_addr_channel(p, DC_ISAE(p->channels), u, 0);
}
- return dst;
+ if ((c = cJSON_GSV(cJSON_GOI(s, "name")))) {
+ free(u->name);
+ u->name = strdup(c);
+ }
+ if ((o = cJSON_GOI(s, "type")))
+ u->type = cJSON_GNV(o);
+ return u;
}
static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, void * in, size_t len) {
struct dc_lws_pass * pass = (struct dc_lws_pass *) us;
- unsigned char buf[LWS_PRE+DC_LWS_BUF+1]; /* whooh, boy, this is more than a meg of stack! */
+ unsigned char buf[LWS_PRE+DC_LWS_BUF+1];
switch (rs) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: /* TODO: handle and report somehow */
if (pass) {
@@ -250,78 +278,37 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us,
pass->api_io.client->ping_interval = cJSON_GNV(obj)/1000-1;
pass->api_io.client->last_ping = 1;
}
-#define DC_PARSEOBJ(w, object, also) if ((obj = object)) { \
- struct dc_##w * w; \
- if ((w = dc_parse_##w(NULL, obj))) \
- w = dc_addr_##w(pass->api_io.program, \
- DC_ISAE(pass->api_io.program->w##s), w, \
- DC_MAY_FREE | DC_REPLACE | DC_INCOMPLETE);\
- also \
- }
- DC_PARSEOBJ(user, cJSON_GOI2(json, "d", "user"),
- if (!pass->api_io.client->user)
- pass->api_io.client->user = user;
- )
- DC_PARSEOBJ(user, cJSON_GOI3(json, "d", "member", "user"),)
- DC_PARSEOBJ(user, cJSON_GOI2(json, "d", "author"),)
- DC_PARSEOBJ(user, cJSON_GOI3(json,
- "d", "referenced_message", "author"),)
-#define DC_PARSEARR(what, arr, also) cJSON_AFE(obj, arr) { \
- struct dc_##what * what = dc_parse_##what(NULL, obj); \
- if (!what) \
- continue; \
- dc_addr_##what(pass->api_io.program, \
- DC_ISAE(pass->api_io.program->what##s), \
- what, DC_MAY_FREE|DC_REPLACE|DC_INCOMPLETE);\
- also \
- }
- DC_PARSEARR(user, cJSON_GOI2(json, "d", "users"),)
- DC_PARSEARR(user, cJSON_GOI2(json, "d", "mentions"),)
- DC_PARSEARR(user, cJSON_GOI3(json,
- "d", "referenced_message", "mentions"),)
- cJSON_AFE(obj, cJSON_GOI2(json, "d", "private_channels")) {
- if (/* !cJSON_GAS(cJSON_GOI(obj, "recipient_ids")) || */
- /* commented. DMs with 0 members're shown /\ */ !cJSON_GSV(cJSON_GOI(obj, "id")) ||
- !DC_CHANNEL_SUPPORTED(
- /* cJSON is called many times here but I don't care */ cJSON_GNV(cJSON_GOI(obj, "type"))))
- continue;
- struct dc_channel * ch = dc_channel_init();
- if ((st = cJSON_GSV(cJSON_GOI(obj, "name"))))
- ch->name = strdup(st);
- ch->type = cJSON_GNV(cJSON_GOI(obj, "type"));
- ch->id = strtoull(cJSON_GSV(cJSON_GOI(obj, "id")), NULL, 10);
- cJSON_AFE(obje, cJSON_GOI(obj, "recipient_ids")) {
- if (!(st = cJSON_GSV(obje)))
- continue;
- struct dc_user * part = dc_user_init();
- part->id = strtoull(st, NULL, 10);
- part = dc_add_user(
- DC_ISAE(pass->api_io.program->users),
- /* no replace here. stored user can be better. */ part, DC_MAY_FREE);
- DC_MR(ch->users); /* needn't dc_add here coz start */
- ch->users[ch->users_length++] = part; /* empty ch. */
- }
- ch = dc_addr_channel(pass->api_io.program,
- DC_ISAE(pass->api_io.program->channels), ch,
- /* replace here. ours is better - fresher. */ DC_MAY_FREE | DC_REPLACE | DC_INCOMPLETE);
- ch->guild = pass->api_io.client->guilds[0];
- }
+ struct dc_user * user = dc_parse_user(DC_PAIP, cJSON_GOI2(json, "d", "user"));
+ if (!pass->api_io.client->user)
+ pass->api_io.client->user = user;
+ dc_parse_user(DC_PAIP, cJSON_GOI3(json, "d", "member", "user"));
+ dc_parse_user(DC_PAIP, cJSON_GOI2(json, "d", "author"));
+ dc_parse_user(DC_PAIP, cJSON_GOI3(json, "d", "referenced_message", "author"));
+ cJSON_AFE(obj, cJSON_GOI2(json, "d", "users"))
+ dc_parse_user(DC_PAIP, obj);
+ cJSON_AFE(obj, cJSON_GOI2(json, "d", "mentions"))
+ dc_parse_user(DC_PAIP, obj);
+ cJSON_AFE(obj, cJSON_GOI3(json, "d", "referenced_message",
+ "mentions"))
+ dc_parse_user(DC_PAIP, obj);
+ cJSON_AFE(obj, cJSON_GOI2(json, "d", "private_channels"))
+ dc_parse_channel(DC_PAIP, obj);
cJSON_AFE(ob, cJSON_GOI2(json, "d", "merged_members")) {
obj = cJSON_GetArrayItem(ob, 0);
if (!cJSON_GAS(cJSON_GOI(obj, "roles")))
continue;
struct dc_user * user = dc_user_init();
user->id = strtoull(cJSON_GSV(cJSON_GOI(obj, "user_id")),
- NULL, 10);
- user = dc_addr_user(
+ NULL, 10);
+ user = dc_addr_user(DC_PAIP,
DC_ISAE(pass->api_io.program->users), user,
- /* again, no replace here, only ID */ DC_MAY_FREE);
+ DC_MAY_FREE); /* no replace - we only have ID */
cJSON_AFE(obje, cJSON_GOI(obj, "roles")) {
if (!(st = cJSON_GSV(obje)))
continue;
struct dc_role * role = dc_role_init();
role->id = strtoull(st, NULL, 10);
- role = dc_addr_role(
+ role = dc_addr_role(DC_PAIP,
DC_ISAE(pass->api_io.program->roles),
role, DC_MAY_FREE);
/* role may have users. NO FREE! */ dc_add_user(DC_ISAE(role->users), user, DC_UNSET);
diff --git a/src/generate_parse_functions.php b/src/generate_parse_functions.php
new file mode 100755
index 0000000..23eca49
--- /dev/null
+++ b/src/generate_parse_functions.php
@@ -0,0 +1,112 @@
+#!/usr/bin/env php
+<?php
+$s = [
+ "user" => [
+ "members" => [
+ "username" => [
+ "json" => "username",
+ "type" => "string"
+ ],
+ "discriminator" => [
+ "json" => "discriminator",
+ "type" => "number"
+ ]
+ ]
+ ],
+ "channel" => [
+ "members" => [
+ "name" => [
+ "json" => "name",
+ "type" => "string"
+ ],
+ "type" => [
+ "json" => "type",
+ "type" => "number"
+ ]
+ ],
+ "parsing" => [
+ "checks" => [
+ 'DC_CHANNEL_SUPPORTED(cJSON_GNV(cJSON_GOI(s, "type")))'
+ ],
+ "code" => <<<HEREDOC
+ cJSON_AFE(o, cJSON_GOI(s, "recipient_ids")) {
+ char * c = cJSON_GSV(o);
+ if (!c)
+ continue;
+ struct dc_user * u = dc_user_init();
+ u->id = strtoull(st, NULL, 10);
+ u = dc_addr_user(p, DC_ISAE(p->users), u, DC_MAY_FREE);
+ dc_add_user(DC_ISAE(ch->users), u, 0);
+ }
+ HEREDOC,
+ ]
+ ]
+];
+foreach ($s as $name => $fields) {
+ echo <<<HEREDOC
+ struct dc_$name * dc_parse_$name (struct dc_program * p, const cJSON * s) {
+ cJSON * o;
+ if (!s) /* in case cJSON_GOIx returns NULL */
+ return NULL;
+ char * c = cJSON_GSV(cJSON_GOI(s, "id"));
+ if (!c)
+ return NULL;
+ unsigned long long int i = strtoull(c, NULL, 10);
+ struct dc_$name * n = dc_find_$name(p->{$name}s, p->{$name}s_length, i);
+
+ HEREDOC;
+ if (isset($fields["parsing"]) && sizeof($fields["parsing"]["checks"])) {
+ echo " if (";
+ $arr = $fields["parsing"]["checks"];
+ foreach ($arr as $n => $check) {
+ $or = "";
+ $end = "";
+ if ($n != array_key_first($arr))
+ $or = " || ";
+ if ($n != array_key_last($arr))
+ $end = PHP_EOL;
+ echo "$or!($check)$end";
+ }
+ echo <<<HEREDOC
+ )
+ return NULL;
+
+ HEREDOC;
+ }
+ echo <<<HEREDOC
+ if (!n) {
+ n = dc_{$name}_init();
+ n->id = i;
+ dc_addr_$name(p, DC_ISAE(p->{$name}s), n, 0);
+ }
+
+ HEREDOC;
+ if (isset($fields["parsing"]))
+ echo $fields["parsing"]["code"] . PHP_EOL;
+ foreach ($fields["members"] as $member => $meta) {
+ switch ($meta["type"]) {
+ case "string":
+ echo <<<HEREDOC
+ if ((c = cJSON_GSV(cJSON_GOI(s, "{$meta["json"]}")))) {
+ free(n->$member);
+ n->$member = strdup(c);
+ }
+
+ HEREDOC;
+ break;
+ case "number":
+ echo <<<HEREDOC
+ if ((o = cJSON_GOI(s, "{$meta["json"]}")))
+ n->$member = cJSON_GNV(o);
+
+ HEREDOC;
+ break;
+ }
+ }
+ echo <<<HEREDOC
+ return n;
+ }
+
+ HEREDOC;
+}
+?>
diff --git a/src/h.c b/src/h.c
index 8713d9d..8067c5b 100644
--- a/src/h.c
+++ b/src/h.c
@@ -26,6 +26,7 @@
#else
#define DC_IF_UI_GTK(...)
#endif
+#define DC_PAIP (pass->api_io.program)
/* it's strongly recommended to calloc structs during initialization. */
enum dc_status { /* theese are flags and should be and-checked */
DC_UNSET = 0, /* default value when enum is calloced */ /* \/ USEFUL FOR ->next!!! */
@@ -431,9 +432,9 @@ struct dc_guild {
char * name; /* yesfree */
char * description; /* yesfree */
unsigned long long int id; /* 0 for virtual DMs guild */
- struct dc_channel * channel; /* nofree - first channel */
struct dc_role * role; /* nofree - first role. NOTE: role->id==guild->id => @everyone */
enum dc_status status; /* /\ if NULL then assume all permissions - a DM guild */
+ DC_ISASQ(channel); /* yesfree array only - can't be LL because of DM channels in many g[0] */
#ifdef DC_UI_GTK
GtkTreeIter iter; /* GtkTreeModel needs GTK_TREE_MODEL_ITERS_PERSIST for this to work */
gboolean is_iter; /* see parag 8 file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeModel.html */
@@ -448,6 +449,7 @@ void dc_guild_free (struct dc_guild * s, enum dc_status t) {
return;
free(s->name);
free(s->description);
+ free(s->channels);
if (!(t & DC_REPLACE)) /* we do this because we want to keep the pointer intact sometimes and */
free(s); /* reused; for example when replacing/updating structs */
}
@@ -490,10 +492,9 @@ struct dc_channel {
DC_STRUCT_PREFIX
char * name; /* yesfree - name */
char * topic; /* yesfree - topic */
- unsigned long long int id;
- enum dc_channel_type type;
- struct dc_guild * guild; /* nofree */
- struct dc_channel * next; /* nofree - next channel (linked list of all channel of dc_guild) */
+ unsigned long long int id; /* send message functions must therefore take */
+ enum dc_channel_type type; /* vvv client as parameter to get g[0] */
+ struct dc_guild * guild; /* nofree - in case of DM channel this is NULL, many g[0] same ch */
struct dc_message * message; /* nofree - first message (ordered by time) */
enum dc_status status;
DC_ISASQ(permission); /* yesfree array and members - ch permissions for users/roles */
@@ -817,7 +818,6 @@ void dc_transfer_channel (struct dc_channel * n, struct dc_channel * o) { /* n w
DC_TRANSFER_MEMBER(id)
DC_TRANSFER_MEMBER(type)
DC_TRANSFER_MEMBER(guild)
- DC_TRANSFER_MEMBER(next)
DC_TRANSFER_MEMBER(message)
DC_TRANSFER_ISA(permission)
DC_TRANSFER_ISA(user)
@@ -831,9 +831,9 @@ void dc_transfer_guild (struct dc_guild * n, struct dc_guild * o) {
DC_TRANSFER_MEMBER(name)
DC_TRANSFER_MEMBER(description)
DC_TRANSFER_MEMBER(id)
- DC_TRANSFER_MEMBER(channel)
DC_TRANSFER_MEMBER(role)
DC_TRANSFER_MEMBER(status)
+ DC_TRANSFER_ISA(channel)
DC_IF_UI_GTK(
memmove(&n->iter, &o->iter, sizeof(GtkTreeIter));
n->is_iter = o->is_iter;
@@ -873,7 +873,6 @@ void dc_transfer_user (struct dc_user * n, struct dc_user * o) {
}
DC_GEN_X(user, USER)
DC_GEN_X(channel, CHANNEL)
-DC_FIND_LL_X(channel)
DC_GEN_X(guild, GUILD)
DC_GEN_X(role, ROLE)
DC_FIND_LL_X(role)
diff --git a/src/ui.c b/src/ui.c
index d7feb7e..9e207a6 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -32,10 +32,13 @@ void dc_ui_spawn_message (struct dc_message * m, struct dc_ui_data * d) { /* !m
char t[DC_USMTL];
char * c;
GtkGrid * g = GTK_GRID(gtk_builder_get_object(d->b, "dc_main_messages"));
+ struct dc_guild * guild = m->channel->guild;
+ if (!guild) /* DM message! */
+ guild = d->c->guilds[0];
if (!m) {
while (gtk_grid_get_child_at(g, 0, 0))
gtk_grid_remove_row(g, 0);
- /* struct dc_channel * c = m->channel->guild->channel; */ /* XXX: I wrote this line of code but then I wen't to sleep and I can't remember what should I do here with the channel :shrug: */
+ /* struct dc_channel * c = guild->channel; */ /* XXX: I wrote this line of code but then I wen't to sleep and I can't remember what should I do here with the channel :shrug: */
return;
}
while ((w = gtk_grid_get_child_at(g, 0, i))) { /* now we get the index BEFORE which message will be placed */
@@ -67,7 +70,7 @@ void dc_ui_spawn_message (struct dc_message * m, struct dc_ui_data * d) { /* !m
gtk_container_add(GTK_CONTAINER(b), gtk_label_new(t));
g_object_set_data(G_OBJECT(b), "message", m);
gtk_grid_attach(g /* grid */, b /* widget to insert */, 0 /* left */, i /* top */, 1 /* width */, 1 /* height */);
- /* if (m->user == m->channel->guild->client->user) */ /* TODO: if I posted the message, make it an editable textview */
+ /* if (m->user == guild->client->user) */ /* TODO: if I posted the message, make it an editable textview */
gtk_grid_attach(g, GTK_WIDGET(gtk_label_new(m->message)), 1, i, 1, 1);
gtk_widget_show_all(GTK_WIDGET(g));
gtk_widget_show_all(GTK_WIDGET(g));
@@ -80,21 +83,21 @@ void dc_ui_spawn_channel (struct dc_channel * c /* needs a functional guild or s
gtk_tree_store_clear(l);
for (size_t i = 0; i < d->c->guilds_length; i++) {
d->c->guilds[i]->is_iter = FALSE;
- struct dc_channel * ch = d->c->guilds[i]->channel;
- while (ch) {
- ch->is_iter = FALSE;
- ch = ch->next;
- }
+ for (size_t j = 0; j < d->c->guilds[i]->channels_length; j++)
+ d->c->guilds[i]->channels[j]->is_iter = FALSE;
}
}
- if (!c->guild->is_iter) { /* we don't have this guild already rendered */
+ struct dc_guild * guild = c->guild;
+ if (!guild) /* DM channel! */
+ guild = d->c->guilds[0];
+ if (!guild->is_iter) { /* we don't have this guild already rendered */
gtk_tree_store_insert(l, &i, NULL, -1 /* row position */);
- gtk_tree_store_set(l, &i, 0, c->guild->name, -1);
- gtk_tree_store_set(l, &i, 1, c->guild, -1);
- memcpy(&c->guild->iter, &i, sizeof(GtkTreeIter));
- c->guild->is_iter = TRUE;
+ gtk_tree_store_set(l, &i, 0, guild->name, -1);
+ gtk_tree_store_set(l, &i, 1, guild, -1);
+ memcpy(&guild->iter, &i, sizeof(GtkTreeIter));
+ guild->is_iter = TRUE;
}
- gtk_tree_store_insert(l, &i, &c->guild->iter, -1); /* for this to work, iter must not be same as parent! */
+ gtk_tree_store_insert(l, &i, &guild->iter, -1); /* for this to work, iter must not be same as parent! */
gtk_tree_store_set(l, &i, 0, c->name, -1); /* TODO: set hover tooltip for c->topic */
gtk_tree_store_set(l, &i, 1, c, -1);
memcpy(&c->iter, &i, sizeof(GtkTreeIter));